/* Copyright 2002 -- Ira R. Forman and Nate Forman */ 
/**
 * This transformation generates a proxy class for its input
 * class.  In effect, it is a static substitute for java.lang.Proxy.
 * The transformation generates a class that has a method for each
 * public method of the target class.
 */
//start extract C2ProxyForC
package c2c;
import mopex.Mopex;
import mopex.UQueue;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class C2ProxyForC extends C2CTransformation {

    static public void main( String[] args ) {
	new C2ProxyForC().createClass( args );
    }
    
//stop extract C2ProxyForC
    /**
     * The prefix contributed by this transformation is "ProxyFor".
     */
//start extract C2ProxyForC
    protected String generateClassNamePrefix() {
	return "ProxyFor" + super.generateClassNamePrefix();
    }
    
//stop extract C2ProxyForC
    /**
     *
     */
//start extract C2ProxyForC
    protected void checkAndProcessArgs( Args args ) {                  //#1
	super.checkAndProcessArgs( args );

	if ( !inputClassObject.isInterface() )
	    throw new C2CException( "input class is not an interface" );
    }
    
    protected UQueue generateInterfaces() {                            //#2
	return super.generateInterfaces().add(inputClassName);
    }

//stop extract C2ProxyForC
    /**
     * Generates the class level javadoc statement for the 
     * output class of the transformation.
     */
    protected String getClassLevelJavadoc() {
	return "/**\n"
	    +  " * Creates proxy instances for instances of " + inputClassName + ".\n"
	    +  " * For each public, nonstatic method of "  + inputClassName + ",\n"
	    +  " * there is a public nonstatic method in "  + outputClassName + "\n"
	    +  " * that forwards the call to the target that is established in the constructor.\n"
	    +  " */\n";
    }
    
    /**
     * Adds the private fields target and targetClassName.
     */
//start extract C2ProxyForC
    protected String generateFields() {                                //#3
	return super.generateFields()
	    + "    private " + inputClassName + " target;\n"
	    + "    private static final String targetClassName = \"" 
	    + qualifiedInputClassName + "\";\n";
    }
    
//stop extract C2ProxyForC
    /**
     * A proxy class has one constructor that is used to set the target of a proxy.
     */
//start extract C2ProxyForC
    protected String generateConstructors() {                          //#4
	return super.generateConstructors()
	    + outputClassName + "( " + inputClassName + " tgt ) {\n"
	    + "     target = tgt;\n"
	    + "}\n";
    }
    
//stop extract C2ProxyForC
    /**
     * Generates the methods for the proxy.  Each method forwards its call
     * to the target of the proxy. Forwarding methods are only generated for
     * public, nonstatic methods of the target class.
     */
//start extract C2ProxyForC
    protected String generateMethods() {                               //#5
	String result = "";
	Method[] methods = Mopex.selectMethods( inputClassObject, 
						Modifier.PUBLIC, 
						Modifier.STATIC, 
						Object.class );
	for ( int i = 0; i < methods.length; i++ ) {
	    int mods = Mopex.getModifiersWithout( methods[i], 
						  Modifier.NATIVE | Modifier.ABSTRACT );
	    result += "    " + Modifier.toString(mods) + " " 
		    + Mopex.headerSuffixToString( methods[i] ) + "{\n";
	    Class[] fpl = methods[i].getParameterTypes();
	    String apl = Mopex.actualParametersToString( fpl );
	    if ( methods[i].getReturnType() == void.class ) {
		result += "        target." 
		        + methods[i].getName() 
		        + "(" + apl + ");\n";
	    } else {
		result += "        return target." 
		        + methods[i].getName() 
		        + "(" + apl + ");\n";
	    }
	    result += "    }\n";
	}
	result += 
	    "    public boolean equals( Object obj ) {\n" +
	    "        return target.equals( obj );\n" +
	    "    }\n" +
	    "    public int hashCode() {\n" +
	    "        return target.hashCode();\n" +
	    "    }\n" +
	    "    public String equals() {\n" +
	    "        return target.toString();\n" +
	    "    }\n";
	return super.generateMethods() + result;
    }
    
//stop extract C2ProxyForC
    /**
     * Ensures that the generated class has only the one constructor
     * that is provided by the generateConstructors method.
     */
//start extract C2ProxyForC
    protected void checkPostconditions(){                               //#6
	if ( outputClassObject.getDeclaredConstructors().length != 1 )
	    throw new C2CException("a proxy has only one constructor");
	super.checkPostconditions( );
    }
}
//stop extract C2ProxyForC
